package com.xkcyy.demo.netty.filesrv;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.Headers;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedFile;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.CharsetUtil;
import lombok.SneakyThrows;

import java.io.File;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Arrays;

import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpResponseStatus.*;

/**
 * FileServer
 *
 * @author xkcyy
 * @date 2022/7/13
 **/
public class FileServer {

    @SneakyThrows
    public void run(int port) {
        EventLoopGroup boss = new NioEventLoopGroup(1);
        EventLoopGroup work = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, work)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    //.handler(new LoggingHandler())
                    .childHandler(new ChildChannelHandler());
            final ChannelFuture future = bootstrap.bind(port).sync();
            while (true){
                Thread.sleep(1000L);
            }
            Arrays.sort(new int[],);
            //future.channel().closeFuture();
        } finally {
            boss.shutdownGracefully();
            work.shutdownGracefully();
        }

    }

    static class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline().addLast("http-decoder", new HttpRequestDecoder());
            ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536));
            ch.pipeline().addLast("http-encoder", new HttpResponseEncoder());
            ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
            ch.pipeline().addLast("fileServerHandler", new FileServerHandler());


        }
    }

    static class FileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {


        @Override
        protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
            if (!request.decoderResult().isSuccess()) {
                setError(ctx, BAD_REQUEST);
                return;
            }
            if (!HttpMethod.GET.equals(request.method())) {
                setError(ctx, METHOD_NOT_ALLOWED);
            }
            final String uri = request.uri();
            final File file = new File("D:\\" + sanitizeUri(uri));
            if (file.exists()) {
                RandomAccessFile randomAccessFile = null;
                if (file.isFile()) {
                    try {
                        randomAccessFile = new RandomAccessFile(file, "r");
                        long len = randomAccessFile.length();
                        HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, OK);
                        HttpUtil.setContentLength(response, len);
                        response.headers().set(CONTENT_TYPE, "text");
                        ctx.write(response);
                        final ChannelFuture future = ctx.write(new ChunkedFile(randomAccessFile, 0, len, 2048));
                        //ctx.newProgressivePromise()
                        ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
                    } finally {

                    }
                } else {
                    final File[] files = file.listFiles();
                    final DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, OK);
                    response.headers().set(CONTENT_TYPE, "text/html;charset=UTF-8");
                    StringBuilder sb = new StringBuilder();
                    sb.append("<!DOCTYPE html>\\r\\n");
                    sb.append("<html><body><ul>");
                    for (File f : files) {
                        sb.append("<li><a href=\"./" + f.getName() + "/\">"+ f.getName()+"</a></li>");
                    }
                    sb.append("</ul></body></html>");
                    final ByteBuf buf = Unpooled.copiedBuffer(sb, CharsetUtil.UTF_8);
                    response.content().writeBytes(buf);
//                    ctx.writeAndFlush(response);
                    ctx.writeAndFlush(response).addListener (ChannelFutureListener.CLOSE);
                }
            }
            else{
                final DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, NOT_FOUND);
                ctx.writeAndFlush(RESET_CONTENT).addListener (ChannelFutureListener.CLOSE);
            }
        }

        private String sanitizeUri(String uri) {
            try {
                uri = URLDecoder.decode(uri, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                try {
                    uri = URLDecoder.decode(uri, "ISO-8859-1");
                } catch (UnsupportedEncodingException e1) {
                    throw new Error();
                }
            }
            uri = uri.replace('/', File.separatorChar);
            return uri;
        }

        private void setError(ChannelHandlerContext ctx, HttpResponseStatus code) {

        }
    }
}
